/**
 * 
 */
package cz.cuni.mff.abacs.burglar.logics.planning;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import cz.cuni.mff.abacs.burglar.logics.GameMap;
import cz.cuni.mff.abacs.burglar.logics.objects.agents.Guard;
import cz.cuni.mff.abacs.burglar.logics.planning.instructions.Instruction;
import cz.cuni.mff.abacs.burglar.visual.Main;


// TODO vegere bedobott fuggvenyeket javitani

/** 
 * Handler of the external planner tool.
 * 
 * @author abacs
 *
 */
public class MetricFFHandler extends PlannerHandlerBase implements PlannerHandler {
	
	
	/**  */
	protected static String PLANNER_PATH = Main.PLANNER_PATH + "metricff/";
	
	
	// -------------------------------------------------------------------------
	
	
	@Override
	public List<Instruction> solveProblem(GameMap map) {
		try{
			// get the burglar solution:
			BufferedReader input = MetricFFHandler.solveBurglarProblem_(map, null, null);
			
			List<Instruction> instructions = new ArrayList<Instruction>();
			MetricFFHandler.translateInstructions(input, map, instructions);	
			
			
			
			return instructions;
		}catch(Exception e){
			System.err.println(e.toString());
			e.printStackTrace();
		}
		return new ArrayList<Instruction>();
	}
	
	
	@Override
	public List<Instruction> solveBurglarProblem(
			GameMap map,
			List<Integer> roomsToEnter,
			List<Integer> roomsToAvoid
	) {
		try{
			// get the burglar solution:
			BufferedReader input = MetricFFHandler.solveBurglarProblem_(map, roomsToEnter, roomsToAvoid);
			
			List<Instruction> instructions = new ArrayList<Instruction>();
			MetricFFHandler.translateInstructions(input, map, instructions);
			
			return instructions;
		}catch(Exception e){
			System.err.println(e.toString());
			e.printStackTrace();
		}
		return new ArrayList<Instruction>();
	}
	
	
	// -------------------------------------------------------------------------
	
	
	/**
	 * 
	 * @param map
	 * @param roomsToEnter
	 * @param roomsToAvoid
	 * @return null if the execution was not successful.
	 */
	private static BufferedReader solveBurglarProblem_(
			GameMap map,
			List<Integer> roomsToEnter,
			List<Integer> roomsToAvoid
	) {
		PDDLGenerator.generateBurglarProblem(
				map,
				roomsToEnter,
				roomsToAvoid,
				PROBLEMS_PATH + "burglar.pddl"
		);
		
		BufferedReader input = null;
		try{
			Runtime runtime = Runtime.getRuntime();
			Process process = 
				runtime.exec("make -f " + PLANNER_PATH + "Makefile burglar");
			if(process.waitFor() == EXIT_VALUE_SUCCESS)
				input = 
				new BufferedReader(new InputStreamReader(process.getInputStream()));
			else
				input = null;
		}catch(Exception e){
			System.err.println(e.toString());
			e.printStackTrace();
		}
		return input;
	}
	
	

	
	
	// -------------------------------------------------------------------------
	
	
	/**
	 * Goes though the input string line by line and translates 
	 * the instructions from planner specific string format.
	 * 
	 * @param input file input
	 * @param map 
	 * @param instructions
	 * @throws IOException 
	 */
	private static void translateInstructions(
			BufferedReader input,
			GameMap map,
			List<Instruction> instructions
	) throws IOException {
		if(input == null)
			return;
		String line = null;
		while((line = input.readLine()) != null)
			MetricFFHandler.translateInstruction(line, map, instructions);
	}
	
	
	/**
	 * Translates a single instruction.
	 * 
	 * @param planLine line to translate
	 * @param map 
	 * @param instructions
	 * @return true if agent just entered a new room.
	 */
	private static boolean translateInstruction(
			String planLine,
			GameMap map,
			List<Instruction> instructions
	) {
		// remove the unnecessary chars:
		planLine = planLine.replace("step", "");
		planLine = planLine.trim();
		
		// split into words:
		String[] words = planLine.split(" ");
		
		// translate the instruction code:
		Instruction.code code = MetricFFHandler.getInstructionCode(words[1]);
		
		// get the agent:
		int agentId = -1;
		try{
			agentId = Integer.parseInt(words[2].substring(1));
		}catch(NumberFormatException e){ }
		
		int positionId;
		
		switch(code){
		case COMPLEX_MOVE:
			int moveToId = Integer.parseInt(words[4].substring(1));
			
			instructions.add(new Instruction(code, agentId, moveToId));
			return false;
		case ENTER_DOOR:
			int doorId = Integer.parseInt(words[3].substring(1));
			
			instructions.add(new Instruction(code, agentId, doorId));
			return true;
		case OPEN:
		case CLOSE:
			positionId = Integer.parseInt(words[3].substring(1));
			
			instructions.add(new Instruction(code, agentId, positionId));
			return false;
		case UNLOCK:
		case LOCK:
		case PICK_UP:
			positionId = Integer.parseInt(words[3].substring(1));
			int itemId = Integer.parseInt(words[4].substring(1));
			
			instructions.add(
					new Instruction(code, agentId, positionId, itemId)
			);
			return false;
		default:
			
		}
		return false;
	}


	@Override
	public List<Instruction> solveGuardProblem(Guard guard, GameMap map,
			List<Integer> roomsToEnter, List<Integer> roomsToAvoid) {
		// TODO Auto-generated method stub
		return null;
	}
	
}
